/* Gauge - Performance Measurement Framework for Mobile Platforms
 * Copyright (C) 2013, Juha Hytonen <juha.a.hytonen@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.gauge.GaugeAgent;

import org.gauge.AbstractConnectionManager.ConnectionManagerCb;
import org.gauge.BenchmarkController;
import org.gauge.BenchmarkController.BenchmarkControllerCb;

import java.text.ParseException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;

/**
 * Agent user interface.
 */
public final class GaugeAgent extends Activity 
	implements ConnectionManagerCb, BenchmarkControllerCb, ServiceConnection {

    private BenchmarkController benchmarkController;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        if(savedInstanceState != null) {
        	setIpAddress(savedInstanceState.getString("ipAddress"));
        	setPort(savedInstanceState.getInt("port"));
        } // No else, no saved instance state.
        
        Intent benchmarkControllerIntent = new Intent(this, BenchmarkControllerService.class);
        bindService(benchmarkControllerIntent, this, Context.BIND_AUTO_CREATE); // -> onServiceConnected()
    } 
    
    @Override
    public void onServiceConnected(ComponentName className, IBinder binder) {
    	benchmarkController = ((BenchmarkControllerService.BenchmarkControllerBinder)binder).getService();
    	benchmarkController.SetCallbacks((BenchmarkControllerCb)this, (ConnectionManagerCb)this);
        uiSetReadyToConnect();
    }
    
    @Override
    protected void onDestroy() {
    	unbindService(this); // -> onServiceDisconnected()
    	super.onDestroy();
    }
    
    public void onServiceDisconnected(ComponentName className) {
    	benchmarkController = null;
    }
    
    @Override
    protected void onSaveInstanceState(Bundle outState) {
    	super.onSaveInstanceState(outState);
    	try {
    		outState.putString("ipAddress", getIpAddress());
    		outState.putInt("port", getPort());
    	}
    	catch(ParseException e) { // Not interested in exceptions here.
    	}
    }

    @Override
	public void RunningBenchmarksEvent() {
        runOnUiThread(new Runnable() {
            public void run() {
            	uiSetWaiting(getString(R.string.status_running_benchmarks));
            }
        });
	}

    @Override
	public void RunningBenchmarkEvent(final String name) {    	
        runOnUiThread(new Runnable() {
            public void run() {
                showStatusMessage(name);
            }
        });
	}

    @Override
	public void StoppedEvent() {    	
        runOnUiThread(new Runnable() {
            public void run() {
                uiSetConnected();
                showStatusMessage(getString(R.string.status_benchmarks_finished));
            }
        });
	}

    public void ConnectButtonClicked(View pView) {
    	try {
    		String ipAddress = getIpAddress();
    		int port = getPort();
            uiSetWaiting(getString(R.string.status_connecting));
        	benchmarkController.ConnectToServer(ipAddress, port);
        }
    	catch(ParseException e) {
    	}
    }

    public void DisconnectButtonClicked(View pView) {
    	benchmarkController.DisconnectFromServer();
    }

    public void RunButtonClicked(View pView) {
        benchmarkController.RunBenchmarks();
    }
    
    @Override
    synchronized public void ConnectedEvent() {
        runOnUiThread(new Runnable() {
            public void run() {
                uiSetConnected();
            }
        });
    }

    @Override
    synchronized public void DisconnectedEvent() {
        runOnUiThread(new Runnable() {
            public void run() {
                uiSetReadyToConnect();
            }
        });
    }

	@Override
	public void DataEvent(String pData) { // Intentionally empty. No incoming data.
	}

	@Override
	public void ConnectionFailedEvent() {
        runOnUiThread(new Runnable() {
            public void run() {
                uiSetReadyToConnect();
                showStatusMessage(getString(R.string.status_connection_failed));
            }
        });
	}

    private String getIpAddress() throws ParseException {
        String ipAddress = ((EditText)findViewById(R.id.ipAddress)).getText().toString();
        Pattern ipAddressPattern = Pattern.compile("(\\d{1,3}\\.){3}\\d{1,3}"); 
        Matcher matcher = ipAddressPattern.matcher(ipAddress);
        if (matcher.matches()) {
            return ipAddress;
        }
        else {
            throw new ParseException(getString(R.string.error_invalid_ip_address), 0);
        }
    }

    private void setIpAddress(String ipAddress) {
        ((EditText)findViewById(R.id.ipAddress)).setText(ipAddress);
    }

    private int getPort() throws ParseException {
        try {
            int port = Integer.parseInt(((EditText)findViewById(R.id.port)).getText().toString());
        	return port;
        }
        catch(NumberFormatException e) {
            throw new ParseException(getString(R.string.error_invalid_port), 0);
        }
    }

    private void setPort(int port) {
    	String portString = String.valueOf(port);
        ((EditText)findViewById(R.id.port)).setText(portString);
    }
    
    private void uiSetReadyToConnect() {
    	hideProgressBar();
    	hideStatusMessage();
    	enableConnectionInputs();
    	toggleConnectionControls(true);
    	disableRunControls();
    }

    private void uiSetWaiting(String statusMessage) {
    	showProgressBar();
    	showStatusMessage(statusMessage);
    	disableConnectionInputs();
    	disableConnectionControls();
    	disableRunControls();
    }
    
    private void uiSetConnected() {
    	hideProgressBar();
    	showStatusMessage(getString(R.string.status_connected));
    	disableConnectionInputs();
    	toggleConnectionControls(false);
    	enableRunControls();
    }

    private void showProgressBar() {
    	((ProgressBar)findViewById(R.id.progressBar)).setVisibility(View.VISIBLE);
    }
    
    private void hideProgressBar() {
        ((ProgressBar)findViewById(R.id.progressBar)).setVisibility(View.INVISIBLE);
    }
    
    private void enableConnectionInputs() {
        ((TextView)findViewById(R.id.connectToServerTitle)).setEnabled(true);
        ((EditText)findViewById(R.id.ipAddress)).setEnabled(true);
        ((EditText)findViewById(R.id.port)).setEnabled(true);
    }
    
    private void disableConnectionInputs() {
        ((TextView)findViewById(R.id.connectToServerTitle)).setEnabled(false);
        ((EditText)findViewById(R.id.ipAddress)).setEnabled(false);
        ((EditText)findViewById(R.id.port)).setEnabled(false);
    }
    
    private void toggleConnectionControls(boolean connectEnabled) {
        ((Button)findViewById(R.id.connectButton)).setEnabled(connectEnabled);
        ((Button)findViewById(R.id.disconnectButton)).setEnabled(!connectEnabled);
    }
    
    private void disableConnectionControls() {
        ((Button)findViewById(R.id.connectButton)).setEnabled(false);
        ((Button)findViewById(R.id.disconnectButton)).setEnabled(false);
    }
    
    private void enableRunControls() {
        ((TextView)findViewById(R.id.runTitle)).setEnabled(true);
        ((Button)findViewById(R.id.runButton)).setEnabled(true);
    }
    
    private void disableRunControls() {
        ((TextView)findViewById(R.id.runTitle)).setEnabled(false);
        ((Button)findViewById(R.id.runButton)).setEnabled(false);
    }

    private void showStatusMessage(String message) {
        TextView textView = ((TextView)findViewById(R.id.statusMessage));
        textView.setText(message);
        textView.setVisibility(View.VISIBLE);
    }

	private void hideStatusMessage() {
        TextView textView = ((TextView)findViewById(R.id.statusMessage));
        textView.setVisibility(View.INVISIBLE);
	}

}
